package com.xayy.mgt.service.impl;

import cn.hutool.core.date.CalendarUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import cn.kawins.base.result.CheckResult;
import cn.kawins.base.utils.BeanUtil;
import cn.kawins.base.utils.CollUtil;
import cn.kawins.base.utils.HttpUtil;
import cn.kawins.base.utils.StringUtil;
import cn.kawins.mybatis.util.SnowflakeId;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xayy.core.constant.BayesArgs;
import com.xayy.core.constant.ConfigCalPath;
import com.xayy.core.model.Page;
import com.xayy.core.utils.PageHelper;
import com.xayy.mgt.enums.HealthStatu;
import com.xayy.mgt.mapper.BayesHealthMapper;
import com.xayy.mgt.mapper.BayesMapper;
import com.xayy.mgt.model.*;
import com.xayy.mgt.model.dto.TelemetryDto;
import com.xayy.mgt.model.vo.*;
import com.xayy.mgt.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Nie
 * @version V1.0
 * @date 2023/9/14 16:14
 */
@Service
@Slf4j
public class BayesServiceImpl implements BayesService {

    @Autowired
    private NodeParamInfoService nodeParamInfoService;
    @Autowired
    private HistoryFaultService historyFaultService;
    @Autowired
    private BayesArgs bayesArgs;
    @Autowired
    private BayesHealthMapper bayesHealthMapper;
    @Autowired
    private BayesHealthRecordService bayesHealthRecordService;
    @Autowired
    private BayesFaultInfoService bayesFaultInfoService;
    @Autowired
    private NodeInfoService nodeInfoService;
    @Autowired
    private SystemTelemetryService systemTelemetryService;

    @Resource
    private BayesMapper bayesMapper;

    @Autowired
    private ConfigCalPath configCalPath;

    @Override
    public CheckResult<Page<LinkedHashMap<String, Object>>> getErrorDataInfos(DataInfoVo dataInfoVo) {
        if (dataInfoVo.getPageStart() == null || dataInfoVo.getPageSize() == null)
            return CheckResult.fail("页起始数和每页条数不能为空");
        CheckResult<List<LinkedHashMap<String, Object>>> errorList = errorList(dataInfoVo);
        if (errorList.no()) return CheckResult.fail(errorList.getCheckMsg());
        List<LinkedHashMap<String, Object>> data = errorList.getData();
        List<HeaderVo> errorHeader = nodeParamInfoService.getNodeHeader(dataInfoVo.getNodeId(), 1).getData();
        PageHelper<LinkedHashMap<String, Object>> helper = new PageHelper<>(data, dataInfoVo.getPageSize());
        Page<LinkedHashMap<String, Object>> page = helper.getPage(dataInfoVo.getPageStart());
        page.setTableHeader(errorHeader);
        return CheckResult.success(page);

    }

    @Override
    public CheckResult<Page<LinkedHashMap<String, Object>>> getDataInfos(DataInfoVo dataInfoVo) {
        if (dataInfoVo.getPageStart() == null || dataInfoVo.getPageSize() == null){
            dataInfoVo.setPageStart(1);
            dataInfoVo.setPageSize(10);
        }
        if(dataInfoVo.getPageStart() == 0){
            dataInfoVo.setPageStart(1);
        }
        CheckResult<List<LinkedHashMap<String, Object>>> dataList = dataList(dataInfoVo);
        if (dataList.no()) return CheckResult.fail(dataList.getCheckMsg());
        List<LinkedHashMap<String, Object>> data = dataList.getData();
        List<HeaderVo> dataHeader = nodeParamInfoService.getNodeHeader(dataInfoVo.getNodeId(), 2).getData();
        PageHelper<LinkedHashMap<String, Object>> helper = new PageHelper<>(data, dataInfoVo.getPageSize());
        Page<LinkedHashMap<String, Object>> page = helper.getPage(dataInfoVo.getPageStart());
        page.setTableHeader(dataHeader);
        return CheckResult.success(page);
    }

    @Override
    public CheckResult<Object> getNodeHeader(Long nodeId) {
        // 查询表头
        List<HeaderVo> errorHeader = nodeParamInfoService.getNodeHeader(nodeId, 1).getData();
        List<HeaderVo> header = nodeParamInfoService.getNodeHeader(nodeId, 2).getData();
        Map<String, Object> result = new HashMap<>(2);
        result.put("errorHeader", errorHeader);
        result.put("dataHeader", header);
        return CheckResult.success(result);
    }

    @Override
    @Transactional
    public CheckResult<Object> computeHealth(DataInfoVo dataInfoVo) {
        CheckResult<List<LinkedHashMap<String, Object>>> errorList = errorList(dataInfoVo);
        if (errorList.no()) {
            OutInterfaceServiceImpl.addBayesLogs(errorList.getCheckMsg());
            return CheckResult.fail(errorList.getCheckMsg());
        };
        CheckResult<List<LinkedHashMap<String, Object>>> dataList = dataList(dataInfoVo);
        if (dataList.no()){
            OutInterfaceServiceImpl.addBayesLogs(dataList.getCheckMsg());
            return CheckResult.fail(dataList.getCheckMsg());
        }
        // 查询节点名称
        NodeTreeInfo nodeTreeInfo = nodeInfoService.selectById(dataInfoVo.getNodeId());
        if (BeanUtil.isEmpty(nodeTreeInfo)) {
            OutInterfaceServiceImpl.addBayesLogs("节点不存在");
            return CheckResult.fail("节点不存在");
        }
        OutInterfaceServiceImpl.addBayesLogs("故障采样数据数量："+errorList.getData().size());
        OutInterfaceServiceImpl.addBayesLogs("正常采样数据数量："+dataList.getData().size());
        // 查询节点故障
        List<HistoryFault> historyFaults = historyFaultService.select(HistoryFault.builder().nodeId(dataInfoVo.getNodeId()).build());
        Set<String> faultNames = historyFaults.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(HistoryFault::getExceptionName))), ArrayList::new)).stream().map(HistoryFault::getExceptionName).collect(Collectors.toSet());
        Map<String, Object> map = new HashMap<>();
        errorList.getData().addAll(dataList.getData());
        map.put("moduleList", errorList.getData());
        map.put("faultNames", faultNames);
        List<LinkedHashMap<String, Object>> data = computeList(dataInfoVo).getData();
        OutInterfaceServiceImpl.addBayesLogs("遥测数据数量："+data);
        map.put("dataList", data);
        String url = bayesArgs.getAlgorithmUrl();
        try {
            OutInterfaceServiceImpl.addBayesLogs("调用算法："+url);
            HttpResponse postResponse = HttpUtil.createRequest(Method.POST, url).body(JSON.toJSONString(map)).execute();
            AlgorithmResponseVo algorithmResponseVo = JSONObject.parseObject(postResponse.body(), AlgorithmResponseVo.class);
            if (postResponse.getStatus() != 200) {
                OutInterfaceServiceImpl.addBayesLogs("调用算法异常："+algorithmResponseVo.getMsg());
                log.error("算法报错了");
                return CheckResult.fail(algorithmResponseVo.getMsg());
            }
            OutInterfaceServiceImpl.addBayesLogs("调用算法结果："+algorithmResponseVo);
            BayesResponseVo responseVoData = algorithmResponseVo.getData();
            // 修改节点健康度信息
            BayesHealth bayesHealth = getNodeHealthInfo(dataInfoVo.getSatelliteId(), dataInfoVo.getNodeId()).getData();
            bayesHealth.setHealthScore(responseVoData.getHealthGrade());
            bayesHealth.setHealthTime(dataInfoVo.getHealthTime());
            String level = HealthStatu.valueof(responseVoData.getHealthGrade());
            if (level == null) return CheckResult.fail("计算出得分值不在0到100");
            bayesHealth.setHealthLevel(level);
            bayesHealthMapper.updateByPrimaryKey(bayesHealth);
            BayesHealthVo bayesHealthVo = BeanUtil.copyBean(bayesHealth, BayesHealthVo.class);
            bayesHealthVo.setFaultList(CollUtil.newArrayList());
            insetHealthInfo(responseVoData, bayesHealth, bayesHealthVo);
            insertRecord(bayesHealth, nodeTreeInfo, responseVoData);
            return CheckResult.success(bayesHealthVo);
        } catch (Exception e) {
            log.error("调用算法报错了");
            return CheckResult.fail("调用算法报错了");
        }
    }

    private CheckResult<BayesHealthVo> getNodeHealthInfo(Long satelliteId, Long nodeId) {
        BayesHealth bayesHealth = bayesHealthMapper.selectOne(BayesHealth.builder().satelliteId(satelliteId).nodeId(nodeId).build());
        if (BeanUtil.isEmpty(bayesHealth)) {
            // 第一次新建信息
            bayesHealth = new BayesHealth();
            bayesHealth.setSatelliteId(satelliteId);
            bayesHealth.setNodeId(nodeId);
            bayesHealth.setWeight(0D);
            // 插入信息
            bayesHealthMapper.insert(bayesHealth);
        }
        BayesHealthVo bayesHealthVo = BeanUtil.copyBean(bayesHealth, BayesHealthVo.class);
        List<BayesFaultInfo> faultInfos = bayesFaultInfoService.select(BayesFaultInfo.builder().parentId(bayesHealth.getId()).type(1).build());
        bayesHealthVo.setFaultList(faultInfos);
        return CheckResult.success(bayesHealthVo);

    }

    private void insetHealthInfo(BayesResponseVo responseVoData, BayesHealth bayesHealth, BayesHealthVo bayesHealthVo) {
        // 删除故障概率信息，插入新的概率信息
        bayesFaultInfoService.delete(BayesFaultInfo.builder().parentId(bayesHealth.getId()).type(1).build());
        if (CollUtil.isNotEmpty(responseVoData.getFaultList())) {
            List<BayesFaultInfo> bayesFaultInfoList = new ArrayList<>();
            for (BayesFaultVo bayesFaultVo : responseVoData.getFaultList()) {
                BayesFaultInfo bayesFaultInfo = new BayesFaultInfo();
                bayesFaultInfo.setParentId(bayesHealth.getId());
                bayesFaultInfo.setFaultName(bayesFaultVo.getFaultName());
                String possibility = bayesFaultVo.getPossibility();
                double a;
                try {
                    a = Double.parseDouble(possibility);
                } catch (Exception e) {
                    a = 0D;
                }
                bayesFaultInfo.setFaultPossibility(a);
                bayesFaultInfo.setType(1);
                bayesFaultInfoList.add(bayesFaultInfo);
            }
            bayesFaultInfoService.insertList(bayesFaultInfoList);
            bayesHealthVo.setFaultList(bayesFaultInfoList);
        }
    }

    private void insertRecord(BayesHealth bayesHealth, NodeTreeInfo nodeTreeInfo, BayesResponseVo responseVoData) {
        BayesHealthRecord bayesHealthRecord = BeanUtil.copyBean(bayesHealth, BayesHealthRecord.class);
        Long id = new SnowflakeId().genId("bayes_health_record", "id");
        bayesHealthRecord.setId(id);
        bayesHealthRecord.setNodeName(nodeTreeInfo.getNodeName());
        bayesHealthRecordService.insert(bayesHealthRecord);
        // 删除故障概率信息，插入新的概率信息
        if (CollUtil.isNotEmpty(responseVoData.getFaultList())) {
            List<BayesFaultInfo> bayesFaultInfoList = new ArrayList<>();
            for (BayesFaultVo bayesFaultVo : responseVoData.getFaultList()) {
                BayesFaultInfo bayesFaultInfo = new BayesFaultInfo();
                bayesFaultInfo.setParentId(id);
                bayesFaultInfo.setFaultName(bayesFaultVo.getFaultName());
                String possibility = bayesFaultVo.getPossibility();
                double a;
                try {
                    a = Double.parseDouble(possibility);
                } catch (Exception e) {
                    a = 0D;
                }
                bayesFaultInfo.setFaultPossibility(a);
                bayesFaultInfo.setType(2);
                bayesFaultInfoList.add(bayesFaultInfo);
            }
            bayesFaultInfoService.insertList(bayesFaultInfoList);
        }
    }


    /**
     * 获取异常数据
     */
    @Override
    public CheckResult<List<LinkedHashMap<String, Object>>> errorList(DataInfoVo dataInfoVo) {
        List<LinkedHashMap<String, Object>> result = new LinkedList<>();
        // 查询节点参数
        List<NodeParamInfo> nodeParamInfos = nodeParamInfoService.select(NodeParamInfo.builder().nodeId(dataInfoVo.getNodeId()).build());
        if (CollUtil.isNotEmpty(nodeParamInfos)) {
            // 获取遥测表信息
            CheckResult<String> nodeTelemetryName = systemTelemetryService.getNodeTelemetryName(dataInfoVo.getNodeId());
            if (nodeTelemetryName.no())
                return CheckResult.fail(nodeTelemetryName.getCheckMsg());
            String telemetryName = nodeTelemetryName.getData();

            // 查询节点故障
            List<HistoryFault> historyFaults = historyFaultService.select(HistoryFault.builder().nodeId(dataInfoVo.getNodeId()).build());
            if (CollUtil.isNotEmpty(historyFaults)) {
                for (HistoryFault historyFault : historyFaults) {
                    StringBuilder sql = new StringBuilder();
                    StringBuilder params = new StringBuilder();
                    Map<String, String> map = new HashMap<>();
                    for (int i = 0; i < nodeParamInfos.size(); i++) {
                        String key = "key" + i;
                        sql.append("`");
                        sql.append(nodeParamInfos.get(i).getCode());
                        sql.append("`");
                        sql.append(" as ");
                        sql.append("\""+key+"\"");
                        sql.append(" , ");

                        params.append("`");
                        params.append(nodeParamInfos.get(i).getCode());
                        params.append("`");
                        params.append(" , ");
                        map.put(key, nodeParamInfos.get(i).getCode());
                    }

                    Date startTime = historyFault.getExceptionTime();
                    String persistenceTime = historyFault.getPersistenceTime();
                    double aDouble;
                    try {
                        aDouble = Double.parseDouble(persistenceTime);
                    } catch (Exception e) {
                        log.error("异常时间不能转化");
                        continue;
                    }
                    Calendar calendar = CalendarUtil.calendar(startTime);
                    calendar.add(Calendar.SECOND, (int) (aDouble * 60));
                    Date endTime = calendar.getTime();

                    TelemetryDto telemetryDto = new TelemetryDto();
                    telemetryDto.setTableName("health_xw."+telemetryName);
                    telemetryDto.setSatelliteCode(bayesArgs.getNodeCode());
                    telemetryDto.setSatelliteId(dataInfoVo.getSatelliteId());
                    telemetryDto.setNodeId(dataInfoVo.getNodeId());
                    telemetryDto.setParams(NodeInfoServiceImpl.changeSql(params.toString()));
                    telemetryDto.setParamAlise(NodeInfoServiceImpl.changeSql(sql.toString()));
                    telemetryDto.setStartTime(startTime);
                    telemetryDto.setEndTime(endTime);
                    telemetryDto.setStep(dataInfoVo.getErrorSampling());

                    List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
                    try {
                        telemetryDto.setSourceName(NodeInfoServiceImpl.getDataBaseSource());
                        List<LinkedHashMap<String, Object>> hashMaps = bayesMapper.selectInfo(telemetryDto);
                        for (LinkedHashMap<String, Object> hashMap : hashMaps) {
                            LinkedHashMap<String, Object> o = new LinkedHashMap<>();
                            for (String s : hashMap.keySet()) {
                                String key1 = map.get(s);
                                if (StringUtil.isNotBlank(key1)) {
                                    o.put(key1, hashMap.get(s));
                                } else {
                                    o.put(s, hashMap.get(s));
                                }
                            }
                            maps.add(o);
                        }
                    } catch (Exception e) {
                        log.error(e.getMessage());
                        return CheckResult.fail("遥测表中不存在所查参数");
                    }
                    if (CollUtil.isNotEmpty(maps)) {
                        maps.forEach(x -> {
                            x.put("faultName", historyFault.getExceptionName());
                            x.put("faultLevelString", historyFault.getSeriousDegreeString());
                            x.put("faultLevel", historyFault.getSeriousDegree());
                        });
                    }
                    result.addAll(maps);
                }
            }
        }
        return CheckResult.success(result);
    }

    /**
     * 获取正常数据
     */
    @Override
    public  CheckResult<List<LinkedHashMap<String, Object>>> dataList(DataInfoVo dataInfoVo) {
        List<LinkedHashMap<String, Object>> result = new ArrayList<>();

        // 查询节点参数
        List<NodeParamInfo> nodeParamInfos = nodeParamInfoService.select(NodeParamInfo.builder().nodeId(dataInfoVo.getNodeId()).build());
        if (CollUtil.isNotEmpty(nodeParamInfos)) {
            // 获取遥测表信息
            CheckResult<String> nodeTelemetryName = systemTelemetryService.getNodeTelemetryName(dataInfoVo.getNodeId());
            if (nodeTelemetryName.no())
                return CheckResult.fail(nodeTelemetryName.getCheckMsg());
            String telemetryName = nodeTelemetryName.getData();

            StringBuilder sql = new StringBuilder();
            StringBuilder params = new StringBuilder();
            Map<String, String> map = new HashMap<>();
            for (int i = 0; i < nodeParamInfos.size(); i++) {
                String key = "key" + i;
                sql.append("`");
                sql.append(nodeParamInfos.get(i).getCode());
                sql.append("`");
                sql.append(" as ");
                sql.append("\""+key+"\"");
                sql.append(" , ");

                params.append("`");
                params.append(nodeParamInfos.get(i).getCode());
                params.append("`");
                params.append(" , ");
                map.put(key, nodeParamInfos.get(i).getCode());
            }


            //todo 后期替换
            Date startTime = DateUtil.parse("2023-10-01 00:00:00", "yyyy-MM-dd HH:mm:ss");
            Date endTime = DateUtil.parse("2023-10-12 00:00:00", "yyyy-MM-dd HH:mm:ss");

            TelemetryDto telemetryDto = new TelemetryDto();
            telemetryDto.setTableName("health_xw."+telemetryName);
            telemetryDto.setSatelliteCode(bayesArgs.getNodeCode());
            telemetryDto.setSatelliteId(dataInfoVo.getSatelliteId());
            telemetryDto.setNodeId(dataInfoVo.getNodeId());
            telemetryDto.setParams(NodeInfoServiceImpl.changeSql(params.toString()));
            telemetryDto.setParamAlise(NodeInfoServiceImpl.changeSql(sql.toString()));
            telemetryDto.setStartTime(startTime);
            telemetryDto.setEndTime(endTime);
            telemetryDto.setStep(dataInfoVo.getSampling());
            List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
            try {
                telemetryDto.setSourceName(NodeInfoServiceImpl.getDataBaseSource());
                List<LinkedHashMap<String, Object>> hashMaps = bayesMapper.selectInfo(telemetryDto);
                for (LinkedHashMap<String, Object> hashMap : hashMaps) {
                    LinkedHashMap<String, Object> o = new LinkedHashMap<>();
                    for (String s : hashMap.keySet()) {
                        String key1 = map.get(s);
                        if (StringUtil.isNotBlank(key1)) {
                            o.put(key1, hashMap.get(s));
                        } else {
                            o.put(s, hashMap.get(s));
                        }
                    }
                    maps.add(o);
                }
            } catch (Exception e) {
                log.error(e.getMessage());
                return CheckResult.fail("遥测表中不存在所查参数");
            }
            if (CollUtil.isNotEmpty(maps)) {
                maps.forEach(x -> {
                    x.put("faultName", "正常");
                    x.put("faultLevelString", "正常");
                    x.put("faultLevel", "正常");
                });
            }
            result.addAll(maps);
        }
        return CheckResult.success(result);
    }


    /**
     * 计算入参
     */
    CheckResult<List<LinkedHashMap<String, Object>>> computeList(DataInfoVo dataInfoVo) {
        // 查询节点参数
        List<NodeParamInfo> nodeParamInfos = nodeParamInfoService.select(NodeParamInfo.builder().nodeId(dataInfoVo.getNodeId()).build());
        if (CollUtil.isEmpty(nodeParamInfos)) return CheckResult.fail("没有获取到节点得遥测参数");
//        // 获取遥测表信息
        CheckResult<String> nodeTelemetryName = systemTelemetryService.getNodeTelemetryName(dataInfoVo.getNodeId());
        if (nodeTelemetryName.no())
            return CheckResult.fail(nodeTelemetryName.getCheckMsg());
        String telemetryName = nodeTelemetryName.getData();

        StringBuilder sql = new StringBuilder();
        StringBuilder params = new StringBuilder();
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < nodeParamInfos.size(); i++) {
            String key = "key" + i;
            sql.append("`");
            sql.append(nodeParamInfos.get(i).getCode());
            sql.append("`");
            sql.append(" as ");
            sql.append("\""+key+"\"");
            sql.append(" , ");

            params.append("`");
            params.append(nodeParamInfos.get(i).getCode());
            params.append("`");
            params.append(" , ");
            map.put(key, nodeParamInfos.get(i).getCode());
        }

        TelemetryDto telemetryDto = new TelemetryDto();
        telemetryDto.setTableName("health_xw."+telemetryName);
        telemetryDto.setSatelliteCode(bayesArgs.getNodeCode());
        telemetryDto.setSatelliteId(dataInfoVo.getSatelliteId());
        telemetryDto.setNodeId(dataInfoVo.getNodeId());
        telemetryDto.setHealthTime(dataInfoVo.getHealthTime());
        telemetryDto.setParams(NodeInfoServiceImpl.changeSql(params.toString()));
        telemetryDto.setParamAlise(NodeInfoServiceImpl.changeSql(sql.toString()));
        List<LinkedHashMap<String, Object>> linkedHashMaps = new ArrayList<>();
        try {
            OutInterfaceServiceImpl.addBayesLogs("查询遥测参数："+configCalPath.getTelAndFaultUrl()+"/health-xw-back-moni/v1.0/imitate/getTelemetryData 数据："+telemetryDto);
            List<LinkedHashMap<String, Object>> hashMaps = bayesMapper.selectTelemetryByTime(telemetryDto);
            for (LinkedHashMap<String, Object> hashMap : hashMaps) {
                LinkedHashMap<String, Object> o = new LinkedHashMap<>();
                for (String s : hashMap.keySet()) {
                    String key1 = map.get(s);
                    if (StringUtil.isNotBlank(key1)) {
                        o.put(key1, hashMap.get(s));
                    } else {
                        o.put(s, hashMap.get(s));
                    }
                }
                linkedHashMaps.add(o);
            }

        } catch (Exception e) {
            log.error(e.getMessage());
            return CheckResult.fail("遥测表中不存在所查参数");
        }
        return CheckResult.success(linkedHashMaps);
    }


}
